﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Quicklight.Web {

   /// <summary>
   /// The ObservableProperty class wraps a field of type T and fires an event ValueChanged if the value of the field changes. 
   /// The value of the field is exposed through the Value property. 
   /// The class also stores a Validation class that performs validation on the value wrapped by the ObservableProperty class.
   /// </summary>
   /// <typeparam name="T">Type of the field being wrapped by the class</typeparam>
   public class ObservableProperty<T> : IObservable {
      private T value;
      private Validation validation;

      /// <summary>
      /// Constructs an ObservableProperty object, setting the value of the wrapped field to the value passed in the constructor
      /// Also sets the validation to the default validation, which does no validation
      /// </summary>
      /// <param name="value">Initial value of the wrapped field</param>
      public ObservableProperty(T value) {
         this.value = value;
         this.validation = new Validation(this);
      }

      /// <summary>
      /// Event that is fired when the internal value of the ObservableProperty changes
      /// </summary>
      public event EventHandler ValueChanged;

      /// <summary>
      /// Explicit interface implementation of Value property of IObservable. Returns internal value as object
      /// </summary>
      object IObservable.Value {
         get { 
            return this.value;
         }
      }

      /// <summary>
      /// Gets and sets internal observed field. Fires ValueChanged event if internal value changes
      /// </summary>
      public virtual T Value {
         get { 
            return this.value; 
         }
         set {
            if ((Object)this.value == (Object)value)
               return;

            this.value = value;
            OnValueChanged();
         }
      }

      /// <summary>
      /// Gets and sets validation of ObservableProperty. 
      /// If validation is set, the Observable property of the validation object is set to this ObservableProperty instance
      /// </summary>
      public Validation Validation {
         get {
            return this.validation;
         }
         set {
            this.validation = value;
            this.validation.Observable = this;
         }
      }

      /// <summary>
      /// Parses the string s and sets the value of the observable field to the parsed result, firing the ValueChanged event if the internal value changes.
      /// Only parses primitive types. Override if the internal field is not a primitive type
      /// </summary>
      /// <param name="s">String to parse and update the internal value with</param>
      public virtual void SetValue(string s) {
         if (typeof(T).IsAssignableFrom(typeof(Int16)))
            Value = (T)(Object)Int16.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(UInt16)))
            Value = (T)(Object)UInt16.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Int32)))
            Value = (T)(Object)Int32.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(UInt32)))
            Value = (T)(Object)UInt32.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Int64)))
            Value = (T)(Object)Int64.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(UInt64)))
            Value = (T)(Object)UInt64.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(SByte)))
            Value = (T)(Object)SByte.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Byte)))
            Value = (T)(Object)Byte.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Char)))
            Value = (T)(Object)Char.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Decimal)))
            Value = (T)(Object)(Decimal)Double.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Single)))
            Value = (T)(Object)(Single)Double.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Double)))
            Value = (T)(Object)Double.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(Boolean)))
            Value = (T)(Object)Boolean.Parse(s);
         else if (typeof(T).IsAssignableFrom(typeof(String)))
            Value = (T)(Object)s;
         else if (typeof(T).IsAssignableFrom(typeof(Enum))) 
            Value = (T)(Object)Enum.Parse(typeof(T), s);
      }

      /// <summary>
      /// Returns the result of the ToString method on the internal value
      /// </summary>
      /// <returns>The string representation of the internal value</returns>
      public override string ToString() {
         return value.ToString();
      }

      /// <summary>
      /// Fires the ValueChanged event if there are any subscribers.
      /// </summary>
      protected void OnValueChanged() {
         if (ValueChanged != null)
            ValueChanged(this, EventArgs.Empty);
      }

   }

}
